﻿using PMS.Core.Configuration.CommonEnumConfigs;
using PMS.Core.Infrastructure;
using PMS.Core.Utils;
using PMS.Data.Entities.Customers;
using PMS.Data.Entities.Settings;
using PMS.Services.EntityServices.Common;
using PMS.Services.EntityServices.Customers;
using PMS.Services.EntityServices.Medias;
using PMS.Services.EntityServices.Stores;
using PMS.Services.SharedKernel;
using PMS.Services.Utils;
using PMS.Web.Framework.Models;
using PMS.Web.Models;
using PMS.Web.Models.Customers;
using PMS.Web.Models.Messages;
using PMS.Web.Models.Stores;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text.RegularExpressions;

namespace PMS.Web.Factories.Customers
{
    public class CustomerModelFactory : ICustomerModelFactory
    {
        #region Fields
        private readonly MediaSettings _mediaSettings;
        private readonly CaptchaSettings _captchaSettings;
        private readonly CustomerSettings _customerSettings;
        private readonly IStoreService _storeService;
        private readonly IMediaProvider _mediaProvider;
        private readonly IDateTimeHelper _dateTimeHelper;
        private readonly ICustomerService _customerService;
        private readonly IGenericAttributeService _genericAttributeService;
        private readonly IAclSupportedModelFactory _aclSupportedModelFactory;
        #endregion

        #region Ctor
        public CustomerModelFactory(MediaSettings mediaSettings,
            CaptchaSettings captchaSettings,
            CustomerSettings customerSettings,
            IStoreService storeService,
            IMediaProvider mediaProvider,
            IDateTimeHelper dateTimeHelper,
            ICustomerService customerService,
            IGenericAttributeService genericAttributeService,
            IAclSupportedModelFactory aclSupportedModelFactory)
        {
            _mediaSettings = mediaSettings;
            _captchaSettings = captchaSettings;
            _customerSettings = customerSettings;
            _storeService = storeService;
            _mediaProvider = mediaProvider;
            _dateTimeHelper = dateTimeHelper;
            _customerService = customerService;
            _genericAttributeService = genericAttributeService;
            _aclSupportedModelFactory = aclSupportedModelFactory;
        }
        #endregion

        #region Methods
        /// <summary>
        /// Prepare the login model
        /// 根据登录方式不同初始化不同登录方式所需要数据，可多种方式组合登录
        /// 但多种方式登录需要设置默认登录方式
        /// </summary>
        /// <param name="returnUrl">returnUrl for auto Url redirect after customer login success</param>
        /// <param name="loginType"></param>
        /// <returns>Login model</returns>
        public virtual LoginModel PrepareLoginModel(string returnUrl, LoginType loginType = LoginType.Password)
        {
            var model = new LoginModel
            {
                Account = string.Empty,
                DisplayCaptcha = loginType == LoginType.Password && _captchaSettings.Enabled,
                ExtraText = string.Empty,
                CaptchaCodeEncrypt = string.Empty,
                LoginType = loginType,
                Password = string.Empty,
                RememberMe = false,
                ReturnUrl = returnUrl,
            };
            return model;
        }

        /// <summary>
        /// Prepare the customer register model
        /// </summary>
        /// <param name="model">Customer register model</param>
        /// <returns>Customer register model</returns>
        public virtual RegisterModel PrepareRegisterModel(RegisterModel model)
        {
            if (model == null)
                throw new ArgumentNullException(nameof(model));
            switch (_customerSettings.UserRegistrationType)
            {
                case UserRegistrationType.MobileOnly:
                    model.EmailEnabled = false;
                    model.LoginnameEnabled = false;
                    model.NicknameEnabled = true;
                    model.MobileEnabled = true;
                    model.PasswordEnabled = true; //如有短信服务则可根据需要关闭密码输入
                    break;
                case UserRegistrationType.Standard:
                case UserRegistrationType.EmailValidation: //根据业务需要调整 
                case UserRegistrationType.AdminApproval:   //根据业务需要调整
                default:
                    model.EmailEnabled = true;
                    model.LoginnameEnabled = true;
                    model.NicknameEnabled = true;
                    model.MobileEnabled = true;
                    model.PasswordEnabled = true;
                    break;
            }
            return model;
        }

        /// <summary>
        /// Prepare the change password model
        /// </summary>
        /// <param name="customerId">CustomerIdentifier</param>
        /// <returns>Change password model</returns>
        public virtual ChangePasswordModel PrepareChangePasswordModel(int customerId)
        {
            if (customerId <= 0) 
                throw new ArgumentNullException(nameof(customerId));
            var model = new ChangePasswordModel()
            {
                Id = customerId
            };
            return model;
        }

        /// <summary>
        /// Prepare the password recovery model
        /// </summary>
        /// <param name="model">Password recovery model</param>
        /// <returns>Password recovery model</returns>
        public virtual PasswordRecoveryModel PreparePasswordRecoveryModel()
        {
            var model = new PasswordRecoveryModel();
            model.DisplayCaptcha = _captchaSettings.Enabled;
            model.RecoverByEmail = _customerSettings.UserRegistrationType != UserRegistrationType.MobileOnly;

            return model;
        }

        /// <summary>
        /// Prepare the password recovery confirm model
        /// </summary>
        /// <returns>Password recovery confirm model</returns>
        public virtual PasswordRecoveryConfirmModel PreparePasswordRecoveryConfirmModel(int customerId, string PasswordRecoveryToken)
        {
            var model = new PasswordRecoveryConfirmModel
            {
                Id = customerId,
                PasswordRecoveryToken = PasswordRecoveryToken,
                DisablePasswordChanging = false,
                Result = string.Empty
            };
            return model;
        }

        /// <summary>
        /// Prepare customer search model
        /// </summary>
        /// <param name="searchModel">Customer search model</param>
        /// <returns>Customer search model</returns>
        public virtual CustomerSearchModel PrepareCustomerSearchModel(CustomerSearchModel searchModel, RoleType roleType = RoleType.Guests)
        {
            if (searchModel == null)
                throw new ArgumentNullException(nameof(searchModel));

            //search registered customers by default
            var registeredRole = _customerService.GetCustomerRoleByRoleType(roleType);
            if (registeredRole != null)
                searchModel.SelectedCustomerRoleIds.Add(registeredRole.Id);

            var curAdmin = EngineContext.Current.Resolve<IWorkContext>().CurrentCustomer;
            var allStores = _storeService.GetAllStores();
            bool viewAllStoreCustomers = curAdmin.IsAdministrator(_customerService);
            //系统管理员拥有最高权限，可以查看所有店铺注册会员
            if (viewAllStoreCustomers)
                searchModel.SelectedStoreIds = allStores.Select(s => s.Id).ToList();
            else
            {//其他管理员只能查看本店铺注册会员
                searchModel.SelectedStoreIds = allStores.Where(s => s.Id == curAdmin.RegisteredInStoreId)
                    .Select(s => s.Id).ToList();
            }

            searchModel.AvailableStores = allStores.Select(entity => new StoreSelectModel()
            {
                Id = entity.Id,
                Name = entity.Name,
                Url = entity.Url,
                Order = entity.DisplayOrder,
                Selected = searchModel.SelectedStoreIds.Contains(entity.Id),
                Disabled = !searchModel.SelectedStoreIds.Contains(entity.Id)
            }).ToList();
            
            //prepare available customer roles
            _aclSupportedModelFactory.PrepareModelCustomerRoles(searchModel);

            //prepare page parameters
            searchModel.SetGridPageSize();

            return searchModel;
        }

        /// <summary>
        /// Prepare customer model
        /// </summary>
        /// <param name="model">Customer model</param>
        /// <param name="customer">Customer</param>
        /// <param name="excludeProperties">Whether to exclude populating of some properties of model</param>
        /// <returns>Customer model</returns>
        public virtual CustomerModel PrepareCustomerModel(CustomerModel model, Customer customer, bool excludeProperties = false)
        {
            int avatarPictureId = 0;
            if (customer != null)
            {
                //fill in model values from the entity
                model ??= new CustomerModel();

                model.Id = customer.Id;

                model.AllowSendingOfPrivateMessage = _customerService.IsRegistered(customer);
                model.AllowSendingOfWelcomeMessage = _customerService.IsRegistered(customer)
                    && _customerSettings.UserRegistrationType == UserRegistrationType.AdminApproval;
                model.AllowReSendingOfActivationMessage = _customerService.IsRegistered(customer)
                    && !customer.IsActive 
                    && _customerSettings.UserRegistrationType == UserRegistrationType.EmailValidation;

                //whether to fill in some of properties
                if (!excludeProperties)
                {
                    model.Email = customer.Email;
                    if (model.Email.IndexOf("@current-store-name.com") > 0)
                        model.Email = string.Empty;
                    model.LoginName = customer.LoginName;
                    if (model.LoginName.Equals("customer_" + customer.Mobile, StringComparison.CurrentCultureIgnoreCase))
                        model.LoginName = string.Empty;
                    model.Mobile = customer.Mobile;
                    model.NickName = customer.NickName;
                    model.IsActive = customer.IsActive;
                    avatarPictureId = _genericAttributeService.GetAttribute<int>(customer, CustomerDefaultConfigs.AvatarPictureIdAttribute);

                    if(customer.Id > 0)
                    {
                        model.Gender = _genericAttributeService.GetAttribute<string>(customer, CustomerDefaultConfigs.GenderAttribute);
                        DateTime? birthDay = _genericAttributeService.GetAttribute<DateTime?>(customer, CustomerDefaultConfigs.DateOfBirthAttribute);
                        if (birthDay.HasValue)
                            model.DateOfBirth = _dateTimeHelper.ConvertToStoreTime(birthDay.Value, DateTimeKind.Utc);
                        model.DefaultAddress = _genericAttributeService.GetAttribute<List<string>>(customer, CustomerDefaultConfigs.DefaultAddressIdAttribute); ;
                        model.DefaultAddressDetail = _genericAttributeService.GetAttribute<string>(customer, CustomerDefaultConfigs.DefaultAddressDetailAttribute);
                        model.CustomerRoleNames = string.Join(", ", _customerService.GetCustomerRoles(customer).Select(role => role.DisplayName));

                        model.CreatedOnTime = _dateTimeHelper.ConvertToStoreTime(customer.CreatedOnTimeUtc, DateTimeKind.Utc);
                        model.LastActivityTime = _dateTimeHelper.ConvertToStoreTime(customer.LastActivityTimeUtc, DateTimeKind.Utc);
                        model.LastLoginIpAddress = customer.LastLoginIpAddress;
                        model.LastVisitedPage = _genericAttributeService.GetAttribute<string>(customer, CustomerDefaultConfigs.LastVisitedPageAttribute);
                        model.SelectedCustomerRoleIds = _customerService.GetCustomerRoleIds(customer).ToList();
                        model.RegisteredInStore = _storeService.GetAllStores()
                            .FirstOrDefault(store => store.Id == customer.RegisteredInStoreId)?.Name ?? string.Empty;
                    }
                }
                //prepare reward points model
                //model.DisplayRewardPointsHistory = _rewardPointsSettings.Enabled;
                //if (model.DisplayRewardPointsHistory)
                //    PrepareAddRewardPointsToCustomerModel(model.AddRewardPoints);

                //prepare nested search models
                //PrepareRewardPointsSearchModel(model.CustomerRewardPointsSearchModel, customer);
                //PrepareCustomerAddressSearchModel(model.CustomerAddressSearchModel, customer);
                //PrepareCustomerOrderSearchModel(model.CustomerOrderSearchModel, customer);
                //PrepareCustomerShoppingCartSearchModel(model.CustomerShoppingCartSearchModel, customer);
                //PrepareCustomerActivityLogSearchModel(model.CustomerActivityLogSearchModel, customer);
                //PrepareCustomerBackInStockSubscriptionSearchModel(model.CustomerBackInStockSubscriptionSearchModel, customer);
                //PrepareCustomerAssociatedExternalAuthRecordsSearchModel(model.CustomerAssociatedExternalAuthRecordsSearchModel, customer);
            }
            else
            {
                //whether to fill in some of properties
                if (!excludeProperties)
                {
                    //precheck Registered Role as a default role while creating a new customer through admin
                    var registeredRole = _customerService.GetCustomerRoleByRoleType(RoleType.Guests);
                    if (registeredRole != null)
                        model.SelectedCustomerRoleIds.Add(registeredRole.Id);
                }
                model.AllowSendingOfWelcomeMessage = _customerSettings.UserRegistrationType == UserRegistrationType.AdminApproval;
                model.IsActive = true;
                model.CreatedOnTime = DateTime.Now;
            }

            var avatar = _mediaProvider.MediaService.GetMediaFileUrl(avatarPictureId, _mediaSettings.AvatarPictureSize);
            model.AvatarImage.Id = avatarPictureId;
            model.AvatarImage.ThumbImageUrl = avatar.IsDefault ? "/images/default-avatar.jpg" : avatar.thumbFileUrl;
            model.AvatarImage.ImageUrl = avatar.IsDefault ? "/images/default-avatar.jpg" : avatar.FileUrl;
            //prepare model customer roles
            _aclSupportedModelFactory.PrepareModelCustomerRoles(model);

            return model;
        }

        /// <summary>
        /// Prepare SearchModel For Customer-related-Emails
        /// </summary>
        /// <param name="customer"></param>
        /// <returns>CustomerEmailSearchModel</returns>
        public virtual CustomerEmailSearchModel PrepareCustomerEmailSearchModel(Customer customer)
        {
            var searchModel = new CustomerEmailSearchModel(customer);
            //prepare page parameters
            searchModel.SetGridPageSize();

            return searchModel;
        }
        /// <summary>
        /// Prepare paged customer list model
        /// </summary>
        /// <param name="searchModel">Customer search model</param>
        /// <param name="hideSensitiveInfo">hide SensitiveInfo like Mobile/Email to prevent Data stolen</param>
        /// <returns>Customer list model</returns>
        public virtual CustomerListModel PrepareCustomerListModel(CustomerSearchModel searchModel, bool hideSensitiveInfo = true)
        {
            if (searchModel == null)
                throw new ArgumentNullException(nameof(searchModel));

            //get parameters to filter customers
            DateTime? startDate = null;
            DateTime? endDate = null;
            if(!string.IsNullOrEmpty(searchModel.SearchCreateDayStart))
            {
                var startDateLocal = DateTime.Parse(searchModel.SearchCreateDayStart, CultureInfo.CurrentCulture);
                startDate = _dateTimeHelper.ConvertToUtcTime(startDateLocal);
            }
            if (!string.IsNullOrEmpty(searchModel.SearchCreateDayEnd))
            {
                var endDateLocal = DateTime.Parse(searchModel.SearchCreateDayEnd, CultureInfo.CurrentCulture);
                endDate = _dateTimeHelper.ConvertToUtcTime(endDateLocal);
            }

            //get customers
            var customers = _customerService.GetAllCustomers(createdFromUtc: startDate, createdToUtc: endDate,
                customerRoleIds: searchModel.SelectedCustomerRoleIds.ToArray(),
                email: searchModel.SearchEmail,
                loginname: searchModel.SearchLoginname,
                mobile: searchModel.SearchMobile,
                pageIndex: searchModel.Page - 1, 
                pageSize: searchModel.PageSize);

            //prepare list model
            var model = new CustomerListModel().PrepareToGrid(customers, () =>
            {
                return customers.Select(customer =>
                {
                    //fill in model values from the entity
                    var customerModel = customer.ToModel<CustomerModel>();

                    //Hide customer infos such as Mobile to prevent from Infomation Exporting from webpage
                    customerModel.Mobile = hideSensitiveInfo 
                    ? CommonHelper.HideMobileInfo(customer.Mobile) : customer.Mobile;
                    customerModel.Email = hideSensitiveInfo && !string.IsNullOrEmpty(customer.Email) 
                    ? CommonHelper.HideEmailInfo(customer.Email) : customer.Email;
                    if (customerModel.Email.IndexOf("@current-store-name.com") > 0)
                        customerModel.Email = string.Empty;
                    //IsActive: 密码错误被锁定/正常
                    //customerModel.IsActive = customer.IsActive;
                    //用户列表不传输用户账号密码
                    customerModel.Password = string.Empty;
                    //显示账号注册流程的激活状态：如未激活
                    customerModel.AllowSendingOfWelcomeMessage = _customerService.IsRegistered(customer);
                    customerModel.CreatedOnTime = _dateTimeHelper.ConvertToStoreTime(customer.CreatedOnTimeUtc, DateTimeKind.Utc);
                    customerModel.LastActivityTime = _dateTimeHelper.ConvertToStoreTime(customer.LastActivityTimeUtc, DateTimeKind.Utc);
                    customerModel.RegisteredInStore = _storeService.GetAllStores()
                            .FirstOrDefault(store => store.Id == customer.RegisteredInStoreId)?.Name ?? string.Empty;

                    //用户列表展示用不到，借用存储是否为系统管理员
                    customerModel.AllowSendingOfPrivateMessage = customer.IsAdministrator();
                    //fill in additional values (not existing in the entity)
                    customerModel.Gender = _genericAttributeService.GetAttribute<string>(customer, CustomerDefaultConfigs.GenderAttribute);
                    string birthDayString = _genericAttributeService.GetAttribute<string>(customer, CustomerDefaultConfigs.DateOfBirthAttribute);
                    if (!string.IsNullOrEmpty(birthDayString))
                    {
                        DateTime birthDay = DateTime.Parse(birthDayString, CultureInfo.CurrentCulture);
                        customerModel.DateOfBirth = _dateTimeHelper.ConvertToStoreTime(birthDay, DateTimeKind.Utc);
                    }
                    customerModel.CustomerRoleNames = string.Join(", ", _customerService.GetCustomerRoles(customer).Select(role => role.DisplayName));
                    var avatarPictureId = _genericAttributeService.GetAttribute<int>(customer, CustomerDefaultConfigs.AvatarPictureIdAttribute);
                    //customerModel.AvatarUrl = _mediaProvider.MediaService.GetMediaFileUrl(avatarPictureId, _mediaSettings.AvatarPictureSize).FileUrl;
                    var avatar =  _mediaProvider.MediaService.GetMediaFileUrl(avatarPictureId, _mediaSettings.AvatarPictureSize);
                    customerModel.AvatarImage.Id = avatarPictureId;
                    customerModel.AvatarImage.ThumbImageUrl = avatar.IsDefault ? "/images/default-avatar.jpg" : avatar.thumbFileUrl;
                    customerModel.AvatarImage.ImageUrl = avatar.IsDefault ? "/images/default-avatar.jpg" : avatar.FileUrl;
                    return customerModel;
                });
            });

            return model;
        }

        /// <summary>
        /// Prepare online customer search model
        /// </summary>
        /// <param name="searchModel">Online customer search model</param>
        /// <returns>Online customer search model</returns>
        public virtual OnlineCustomerSearchModel PrepareOnlineCustomerSearchModel(OnlineCustomerSearchModel searchModel)
        {
            if (searchModel == null)
                throw new ArgumentNullException(nameof(searchModel));

            //prepare page parameters
            searchModel.SetGridPageSize();

            return searchModel;
        }

        /// <summary>
        /// Prepare paged online customer list model
        /// </summary>
        /// <param name="searchModel">Online customer search model</param>
        /// <returns>Online customer list model</returns>
        public virtual OnlineCustomerListModel PrepareOnlineCustomerListModel(OnlineCustomerSearchModel searchModel)
        {
            if (searchModel == null)
                throw new ArgumentNullException(nameof(searchModel));

            //get parameters to filter customers
            var lastActivityFrom = DateTime.UtcNow.AddMinutes(-_customerSettings.OnlineCustomerMinutes);

            //get online customers
            var customers = _customerService.GetOnlineCustomers(customerRoleIds: null,
                 lastActivityFromUtc: lastActivityFrom,
                 pageIndex: searchModel.Page - 1, pageSize: searchModel.PageSize);

            var curAdmin = EngineContext.Current.Resolve<IWorkContext>().CurrentCustomer;
            var allStores = _storeService.GetAllStores();
            bool isAdmin = curAdmin.IsAdministrator(_customerService);
            //prepare list model
            var model = new OnlineCustomerListModel().PrepareToGrid(customers, () =>
            {
                return customers.Select(customer =>
                {
                    //fill in model values from the entity
                    var customerModel = new OnlineCustomerModel();
                    customerModel.Id = customer.Id;
                    //convert dates to the user time
                    customerModel.LastActivityDate = _dateTimeHelper.ConvertToStoreTime(customer.LastActivityTimeUtc, DateTimeKind.Utc);

                    //fill in additional values (not existing in the entity)
                    customerModel.CustomerInfo = !string.IsNullOrEmpty(customer.Email)
                        ? !isAdmin ? CommonHelper.HideEmailInfo(customer.Email) : customer.Email
                        : $"未绑定邮箱用户{customer.CustomerGuid}";
                    customerModel.LastIpAddress = _customerSettings.ShowCustomersLocation 
                        ? customer.LastLoginIpAddress : "用户IP地址被设置为不可见";
                    customerModel.RegisteredInStore = allStores.FirstOrDefault(store => store.Id == customer.RegisteredInStoreId)?.Name ?? string.Empty;
                    customerModel.LastVisitedPage = _customerSettings.StoreLastVisitedPage
                        ? _genericAttributeService.GetAttribute<string>(customer, CustomerDefaultConfigs.LastVisitedPageAttribute)
                        : "上次访问页面功能被设置关闭";

                    return customerModel;
                });
            });

            return model;
        }

        #endregion
    }
}
